package com.bugsnag.ohos.reactnative;


import com.bugsnag.ohos.Breadcrumb;
import com.bugsnag.ohos.BreadcrumbType;
import com.bugsnag.ohos.Client;
import com.bugsnag.ohos.Event;
import com.bugsnag.ohos.Logger;
import com.bugsnag.ohos.Notifier;
import com.bugsnag.ohos.OnErrorCallback;
import com.bugsnag.ohos.Plugin;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import com.bugsnag.ohos.Thread;
public class BugsnagReactNativePlugin implements Plugin {

    private final ConfigSerializer configSerializer = new ConfigSerializer();
    private final AppSerializer appSerializer = new AppSerializer();
    private final DeviceSerializer deviceSerializer = new DeviceSerializer();
    private final BreadcrumbSerializer breadcrumbSerializer = new BreadcrumbSerializer();
    private final ThreadSerializer threadSerializer = new ThreadSerializer();

    private boolean ignoreJsExceptionCallbackAdded = false;

    public InternalHooks internalHooks;
    public Client client;
    Logger logger;

    private BugsnagReactNativeBridge observerBridge;
    BugsnagReactNativeBridge.Function1 jsCallback;

    @Override
    public void load(Client client) {
        this.client = client;
        logger = client.logger;
        internalHooks = new InternalHooks(client);

        // register a state observer immediately but only pass events on when JS callback set
        observerBridge = new BugsnagReactNativeBridge(client, jsCallback);
        client.addObserver(observerBridge);
        client.logger.i("Initialized React Native Plugin");
    }

    @Override
    public void unload() {

    }

    private void updateNotifierInfo(Map<String, Object> env) {
        String reactNativeVersion = (String) env.get("reactNativeVersion");
        if (reactNativeVersion != null) {
            client.addRuntimeVersionInfo("reactNative", reactNativeVersion);
        }
        String engine = (String) env.get("engine");
        if (engine != null) {
            client.addRuntimeVersionInfo("reactNativeJsEngine", engine);
        }
        String jsVersion = (String) env.get("notifierVersion");
        Notifier notifier = client.notifier;
        notifier.name = "Bugsnag React Native";
        notifier.url = "https://github.com/bugsnag/bugsnag-js";
        notifier.version = jsVersion;
        notifier.dependencies = new ArrayList((Collection) new Notifier()); // depend on bugsnag-ohos
    }

    private void ignoreJavaScriptExceptions() {
        ignoreJsExceptionCallbackAdded = true;
        this.client.addOnError(new OnErrorCallback() {
            @Override
            public boolean onError(Event event) {
                return event.getErrors().get(0).getErrorClass().equals("com.facebook.react.common.JavascriptException");
            }
        });
    }

    private Map<String, Object> configure(Map<String, Object> env) {
        // requireNotNull(env);
        updateNotifierInfo(env);
        if (!ignoreJsExceptionCallbackAdded) {
            ignoreJavaScriptExceptions();
        }
        Map map = new HashMap<String, Object>();
        configSerializer.serialize(map, client.getConfig());
        return map;
    }

    private void registerForMessageEvents(BugsnagReactNativeBridge.Function1 cb) {
        this.jsCallback = cb;
        client.syncInitialState();
    }

    private void leaveBreadcrumb(Map<String, Object> map) {
        //requireNotNull(map);
        String msg = (String) map.get("message");
        BreadcrumbType type = BreadcrumbType.valueOf(map.get("type").toString().toUpperCase(Locale.US));
        Map<String, Object> obj = map.get("metadata") == null ? new HashMap<String, Object>() : map;
        client.leaveBreadcrumb(msg, obj, type);
    }

    private void startSession() {
        client.startSession();
    }

    private void pauseSession() {
        client.pauseSession();
    }

    private void resumeSession() {
        client.resumeSession();
    }

    private void updateContext(String context) {
        client.setContext(context);
    }

    private void updateCodeBundleId(String id) {
        client.setCodeBundleId(id);
    }

    private void clearMetadata(String section, String key) {
        if (key == null) {
            client.clearMetadata(section);
        } else {
            client.clearMetadata(section, key);
        }
    }

    private void addMetadata(String section, Map<String, Object> data) {
        if (data == null) {
            client.clearMetadata(section);
        } else {
            client.addMetadata(section, data);
        }
    }

    private void updateUser(String id, String email, String name) {
        client.setUser(id, email, name);
    }

    private void dispatch(Map<String, Object> payload) {
        //requireNotNull(payload);
        Collection<String> projectPackages = internalHooks.getProjectPackages(client.getConfig());
        Event event = new EventDeserializer(client, projectPackages).deserialize(payload);

        if (event.getErrors().isEmpty()) {
            return;
        }
        String errorClass = event.getErrors().get(0).getErrorClass();
        if (client.immutableConfig.shouldDiscardError(errorClass)) {
            return;
        }
        client.notifyInternal(event, null);
    }

    private Map<String, Object> getPayloadInfo(boolean unhandled) {
        Map<String, Object> info = new HashMap();
        Map<String, Object> app = new HashMap();

        appSerializer.serialize(app, internalHooks.getAppWithState());
        info.put("app", app);
        Map<String, Object> device = new HashMap();
        deviceSerializer.serialize(device, internalHooks.getDeviceWithState());
        info.put("device", device);
        List<Breadcrumb> breadcrumbs = client.getBreadcrumbs();
        List<Map<String, Object>> mapList = new ArrayList<>();
        for (Breadcrumb breadcrumb : breadcrumbs) {
            Map<String, Object> map = new HashMap<>();
            breadcrumbSerializer.serialize(map, breadcrumb);
            mapList.add(map);
        }
        info.put("breadcrumbs", mapList);
        List<Thread> hooksThreads = internalHooks.getThreads(unhandled);
        List<Map<String, Object>> hooksList = new ArrayList<>();
        for (Thread thread : hooksThreads) {
            Map<String, Object> map = new HashMap<>();
            threadSerializer.serialize(map, thread);
            hooksList.add(map);
        }
        info.put("threads", hooksList);
        info.put("appMetadata", internalHooks.getAppMetadata());
        info.put("deviceMetadata", internalHooks.getDeviceMetadata());
        return info;
    }
}
